#include <iostream>
#include <string>
#include <vector>
#include <boost/filesystem.hpp>
#include "util.hpp"

const std::string srcpath = "/root/doc/html/";
const std::string dstpath = "/root/doc/output/RawDoc.txt";

typedef struct DocInfo
{
    std::string title;
    std::string content;
    std::string url;
} DocInfo_t;

bool GetAllHtmlPath(const std::string &root, std::vector<std::string> *out);

bool Parse(std::vector<std::string> &htmlpath, std::vector<DocInfo_t> *out);

bool SaveFile(const std::string &path, std::vector<DocInfo_t> &input);

int main()
{
    // 0.首先提取递归出所有html文件的文件路径，存到数组中
    std::vector<std::string> htmlpath;
    if (!GetAllHtmlPath(srcpath, &htmlpath))
    {
        std::cerr << "Get all html Path err" << "\n";
    }

    // 1.根据每个html文件的文件路径，将其中的内容做解析
    std::vector<DocInfo_t> NewDoc;
    if (!Parse(htmlpath, &NewDoc))
    {
        std::cerr << "Parse all html err" << "\n";
    }

    // 2.将解析后的数据保存到文件中
    if (!SaveFile(dstpath, NewDoc))
    {
        std::cerr << "Save to File err" << "\n";
    }
    return 0;
}

bool GetAllHtmlPath(const std::string &root, std::vector<std::string> *out)
{
    // 0.定义根目录
    namespace fs = boost::filesystem;
    fs::path root_path(root);

    // 1.判断目录是否存在
    if (!fs::exists(root_path))
    {
        std::cerr << root << "no exist" << "\n";
        return false;
    }

    // 2.定义空迭代器，用于判断递归结束,并开始递归遍历文件或目录
    fs::recursive_directory_iterator end;
    for (fs::recursive_directory_iterator begin(root_path); begin != end; begin++)
    {
        if (!fs::is_regular_file(*begin)) // 不是常规文件
            continue;
        if (fs::extension(*begin) != ".html") // 后缀不为html文件--比较string,而不是直接比较c_str（需要调用strcmp）
            continue;

        // 走到这里一定是我们想要的文件的路径
        (*out).emplace_back(begin->path().string());
    }

    return true;
}

bool Extract_Title(const std::string &src_str, std::string *title)
{
    size_t title_start = src_str.find("<title>");
    if (title_start == std::string::npos)
        return false;
    size_t title_end = src_str.find("</title>");
    if (title_end == std::string::npos)
        return false;

    title_start += std::string("<title>").size();
    if (title_start >= title_end) //?
        return false;
    (*title) = src_str.substr(title_start, title_end - title_start);

    return true;
}

bool Remove_Label(const std::string &src_str, std::string *content)
{
    // 使用状态机来完成去标签
    enum status
    {
        CONTENT,
        LABLE
    };

    enum status s = LABLE;
   
    //首先遍历src_str
       for (auto ch : src_str)
       {
           switch (s)
           {
           case LABLE:
               if(ch == '>')
                   s = CONTENT;
               break;
           case CONTENT:
               if (ch == '<')
                   s = LABLE;
               else
               {
                   if (ch == '\n')
                       (*content) += " ";
                   else
                       (*content) += ch;
               }
           default:
               break;
           }
    
       }
    
    return true;
}

const std::string prefix_url = "https://www.pcre.org/current/doc/html/";
bool Spilt_Url(std::string &raw_url, std::string *actual_url)
{
    size_t pos = raw_url.find(srcpath);
    if (pos == std::string::npos)
        return false;

    (*actual_url) = prefix_url + raw_url.substr(srcpath.size());

    return true;
}

void DebugForParse(const DocInfo_t &doc)
{
    std::cout << "title: " << doc.title << "\n";
    std::cout << "content: " << doc.content << "\n";
    std::cout << "url: " << doc.url << "\n";
}

bool Parse(std::vector<std::string> &htmlpath, std::vector<DocInfo_t> *out)
{
    for (int i = 0; i < htmlpath.size(); ++i)
    {
        // 0.首先读入当前html的所有文件内容
        std::string src_content;
        if (!Util_ns::File_Util::ReadFile(htmlpath[i], &src_content))
            continue;
        // 1.先将title提取出来
        std::string _title;
        if (!Extract_Title(src_content, &_title))
            continue;

        // 2.继续提取内容,去标签
        std::string _content;
        if (!Remove_Label(src_content, &_content))
            continue;

        // 3.拼接得到官网的url
        std::string _url;
        if (!Spilt_Url(htmlpath[i], &_url))
            continue;

        // 4.创建doc对象，并将其添加到输出参数中
        DocInfo_t doc;
        doc.title = std::move(_title);
        doc.content = std::move(_content);
        doc.url = std::move(_url);

        (*out).emplace_back(std::move(doc));

        // DebugForParse((*out)[0]);
        // break;
    }
    return true;
}
#define SEP '\3'
bool SaveFile(const std::string &path, std::vector<DocInfo_t> &input)
{
    // 0.以二进制方式打开文件，不希望进行编码
    std::ofstream ofs(dstpath, std::ios::binary);
    if (!ofs.is_open())
    {
        std::cerr << "open file err,path is " << path << "\n";
        return false;
    }
    // 1.写入内容到文件中
    for (auto &doc : input)
    {
        std::string out_str = doc.title;
        out_str += SEP;
        out_str += doc.content;
        out_str += SEP;
        out_str += doc.url;
        out_str += '\n';

        ofs.write(out_str.c_str(), out_str.size());
    }

    ofs.close();
    return true;
}
